import {
	Component,
	createRef,
	useRef,
	forwardRef,
	useImperativeHandle,
} from 'react'

// 考点1： 子组件设置 ref 引用，然后父组件间通过该 ref 引用子组件里面的方法
class PasswordInput extends Component {
	constructor(props) {
		super(props)
		this.passwordInputRef = createRef()
	}

	getPassword = () => {
		return this.passwordInputRef.current.value
	}

	render() {
		return (
			<div className="border">
				<label htmlFor="">{this.props.label}</label>
				<input type="text" ref={this.passwordInputRef} />
			</div>
		)
	}
}

// 考点2： 子组件通过 forwardRef 转发子组件里面的元素 ref 引用给父组件使用
// function AgeInput(props, ref) {
// 	return (
// 		<div className="border">
// 			<label htmlFor="">{props.label}</label>
// 			<input type="text" ref={ref} />
// 		</div>
// 	)
// }

// 考点2.1：子组件向父组件暴露 ref 自定义对象
function AgeInput(props, ref) {
	const inputRef = useRef(null)
	useImperativeHandle(
		ref,
		() => {
			return {
				focus() {
					inputRef.current.focus()
				},
				scrollIntoView() {
					inputRef.current.scrollIntoView()
				},
			}
		},
		[],
	)

	return (
		<div className="border">
			<label htmlFor="">{props.label}</label>
			<input type="text" ref={inputRef} />
		</div>
	)
}

// 考点3：ref 可以设置为 function，通过 function 设置元素的引用关系（这里就没有 ref 概念）
class BirthInput extends Component {
	constructor(props) {
		super(props)
		this.birthInput = null
	}

	setTextInputRef = (ele) => {
		this.birthInput = ele
	}

	componentDidMount() {
		// React 将在组件挂载时，会调用 ref 回调函数并传入 DOM 元素，当卸载时调用它并传入 null
		this.birthInput.value = '123'
		this.birthInput.focus()
	}

	handleClick = () => {
		console.log('birth', this.birthInput.value)
	}

	render() {
		const { label } = this.props
		return (
			<div className="border">
				<label htmlFor="">{label}</label>
				<input type="text" ref={this.setTextInputRef} />
				<button onClick={this.handleClick}></button>
			</div>
		)
	}
}

// 考点4： hook 中使用 useRef 来引用元素和组件
function CityInput(props) {
	const cityInputRef = useRef(null)

	const handleClick = () => {
		console.log('cityInputRef.current.value', cityInputRef.current.value)
	}

	return (
		<div className="border">
			<label htmlFor="">{props.label}</label>
			<input type="text" ref={cityInputRef} />
			<button onClick={handleClick}>点击城市</button>
		</div>
	)
}

// 考点5： hoc 中结合 forwardRef 转发 refs
const hoc = (WrapComponent) =>
	forwardRef((props, ref) => {
		return (
			<div className="border">
				<WrapComponent {...props} countryInputRef={ref}></WrapComponent>
			</div>
		)
	})

function Country({ label, countryInputRef }) {
	return (
		<div className="border">
			<label htmlFor="">{label}</label>
			<input type="text" placeholder="Country" ref={countryInputRef} />
		</div>
	)
}

export default class RefPage extends Component {
	constructor(props) {
		super(props)
		this.state = { count: 0 }
		this.nameInputRef = createRef()
		this.passwordRef = createRef()
		this.ageInputRef = createRef()
		this.countryInputRef = createRef()
	}

	submit = (e) => {
		const name = this.nameInputRef.current.value
		const password = this.passwordRef.current.getPassword()
		// 父组件引用子组件的 ref 抛出来的方法
		const age = this.ageInputRef.current
		const country = this.countryInputRef.current.value
		const galaxy = this.refs.galaxy.value
		// 考点6：废弃写法，直接在元素上绑定 ref 变量为字符串，然后通过 this.refs 里引用
		const sun = this.refs
		console.log('submit', name, password, age, country, galaxy, sun) // submit 12 13 14 17 18 {galaxy: input}
	}

	render() {
		const AgeWithRef = forwardRef(AgeInput)
		const HocCountry = hoc(Country)
		const { count } = this.state
		console.log('渲染')
		return (
			<div>
				<h3>RefPage</h3>
				<button
					onClick={() =>
						this.setState({ count: this.state.count + 1 })
					}
				>
					click change count {count}
				</button>
				<div className="border">
					<label htmlFor="">姓名</label>
					<input type="text" ref={this.nameInputRef} />
				</div>

				<PasswordInput label="密码" ref={this.passwordRef} />

				<AgeWithRef label="年龄" ref={this.ageInputRef} />

				<BirthInput label="生日" />

				<CityInput label="城市" />

				<HocCountry label="国家" ref={this.countryInputRef} />

				<div className="border">
					<label>星系</label>
					<input type="text" ref="galaxy" />
				</div>

				<button onClick={this.submit}>submit</button>
			</div>
		)
	}
}
